/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.adaptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.osgi.framework.Bundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;
import org.springframework.osgi.extender.internal.dependencies.startup.ContextState;
import org.springframework.osgi.io.OsgiBundleResourcePatternResolver;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.common.Role;
import com.cqcis.uip.base.framework.core.ServiceRole;
import com.cqcis.uip.base.framework.core.ServiceRoleIgnore;
import com.cqcis.uip.base.framework.core.ServiceState;
import com.cqcis.uip.base.framework.core.impl.AcceptorServiceAdaptor;
import com.cqcis.uip.base.framework.core.impl.ConnectorServiceAdaptor;
import com.cqcis.uip.base.framework.core.impl.ServiceStateManage;
import com.cqcis.uip.base.framework.handlers.TableDispatchService;
import com.cqcis.uip.base.framework.utils.AnnotationUtil;
import com.cqcis.uip.base.framework.utils.XmlParseUtil;
import com.cqcis.uip.base.framework.xml.codec.AbstractXmlDecoder;

/**
 * Spring xml OSGI上下文
 * 
 * @author huangmy
 * @date   2008-3-21
 */
public class OsgiBundleXmlApplicationContextAdaptor extends OsgiBundleXmlApplicationContext {	
	private static final String CONFIG_PATH = "META-INF/config/*.xml";
	private static Map<Role, List<ServiceRoleResource>> TYPECONIG_MAP;
	private static Resource FRAMEWORK_CONFIG_RESOURCE; // 系统配置资源
	private static Object xmlLoadLock = new Object();
	
	private ServiceStateManage ssm; // 服务状态管理
	private CurrentBundleDelegatingClassLoader cbdcl; // 当前bundle ClassLoader
	private Bundle currentBundle; // 当前bundle
	
	/**
	 * 资源与服务角色关系描述
	 * 
	 * @author huangmy
	 * @date   2008-6-24
	 */
	private static class ServiceRoleResource {
		private Resource resource; // 资源路径
		private ServiceRole serviceRole; // 服务角色
		
		public ServiceRoleResource(ServiceRole serviceRole, Resource resource) {
			this.serviceRole = serviceRole;
			this.resource = resource;
		}

		public Resource getResource() {
			return resource;
		}
		public void setResource(Resource resource) {
			this.resource = resource;
		}
		public ServiceRole getServiceRole() {
			return serviceRole;
		}
		public void setServiceRole(ServiceRole serviceRole) {
			this.serviceRole = serviceRole;
		}		
	}
	
	/**
	 * 角色与服务角色主叫关系描述
	 * 
	 * @author huangmy
	 * @date   2008-6-24
	 */
	public static class RoleService {
		private Role role; // 角色
		private ServiceRole serviceRole; // 服务角色
		
		public RoleService(Role role, ServiceRole serviceRole) {
			this.role = role;
			this.serviceRole = serviceRole;
		}
		public Role getRole() {
			return role;
		}
		public void setRole(Role role) {
			this.role = role;
		}
		public ServiceRole getServiceRole() {
			return serviceRole;
		}
		public void setServiceRole(ServiceRole serviceRole) {
			this.serviceRole = serviceRole;
		}
	}
	
	public OsgiBundleXmlApplicationContextAdaptor(String[] configLocations, ServiceStateManage ssm, Bundle currentBundle) throws Exception {
		super(configLocations);
		this.ssm = ssm;		
		this.currentBundle = currentBundle;		
		
		// init
		synchronized (xmlLoadLock) {
			initConfigPaths(new OsgiBundleResourcePatternResolver(this.currentBundle));	
		}	
	}
	
	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
		    super.postProcessBeanFactory(beanFactory);
		} catch (Exception e) {
			beanFactory.destroySingletons();
			throw new RuntimeException(e);
		}
	}
	
	@Override
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		try {
		    super.invokeBeanFactoryPostProcessors(beanFactory);
		} catch (Exception e) {
			beanFactory.destroySingletons();
			throw new RuntimeException(e);
		}
	}
	
	@Override
	protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		try {
		    super.registerBeanPostProcessors(beanFactory);
		} catch (Exception e) {
			beanFactory.destroySingletons();
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 初始化配置路径
	 * 
	 * @throws Exception
	 */
	private void initConfigPaths(OsgiBundleResourcePatternResolver resourcePatternResolver) throws Exception {
		if (TYPECONIG_MAP != null) {
			return;
		}
		
		TYPECONIG_MAP = new HashMap<Role, List<ServiceRoleResource>>(); // new
		
		Resource[] resources = resourcePatternResolver.getResources(CONFIG_PATH);
		for (Resource r : resources) {			
			if ( "framework-config.xml".equals(r.getFilename())) {
				FRAMEWORK_CONFIG_RESOURCE = r;
				continue;
			}
			
			RoleService role = getRoleFor(r, OsgiBundleXmlApplicationContextAdaptor.class.getClassLoader());
			if (role != null) {
				List<ServiceRoleResource> srr = TYPECONIG_MAP.get(role);
				if (srr == null) {
					srr = new ArrayList<ServiceRoleResource>();
					TYPECONIG_MAP.put(role.getRole(), srr);
				}
				srr.add(new ServiceRoleResource(role.getServiceRole(), r));
			}
		}
	}
	
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		try {
    		ssm.updateState(getBundle().getBundleId(), ContextState.RESOLVING_DEPENDENCIES);
    		super.refresh();
    	} catch (Throwable t) {
    		// 分析XML抛出异常, 包括类无法找到异常
    		ssm.updateState(getBundle().getBundleId(), ContextState.INTERRUPTED, "OsgiBundleXmlApplicationContext refresh Exception.", t); 
    		
    		throw new RuntimeException(t);
    	}
		
	}
	
    @Override
    public void startRefresh() {
    	try {
    		ssm.updateState(getBundle().getBundleId(), ContextState.RESOLVING_DEPENDENCIES);
    		super.startRefresh();
    	} catch (Throwable t) {
    		// 分析XML抛出异常, 包括类无法找到异常
    		ssm.updateState(getBundle().getBundleId(), ContextState.INTERRUPTED, "OsgiBundleXmlApplicationContext startRefresh Exception.", t); 
    		
    		throw new RuntimeException(t);
    	}    	
    }
    
    @Override
    public void completeRefresh() {
    	try {
    		ssm.updateState(getBundle().getBundleId(), ContextState.DEPENDENCIES_RESOLVED);
    		super.completeRefresh();
    		ssm.updateState(getBundle().getBundleId(), ContextState.STARTED);
    	} catch (Throwable t) {
    		// 分析XML抛出异常, 包括类无法找到异常
    		ssm.updateState(getBundle().getBundleId(), ContextState.INTERRUPTED, "OsgiBundleXmlApplicationContext completeRefresh Exception.", t);
    		
    		throw new RuntimeException(t);
    	}    	
    }
    
    @Override
    public void close() {    	
    	super.close();
    	ssm.updateState(getBundle().getBundleId(), ContextState.STOPPED, "OsgiBundleXmlApplicationContext Closed.");
    	ssm.remove(getBundle().getBundleId());//delete
    }
    
    /**
     * 类加载器加载次序: 
     * 1. 需要创建spring context的bundle
     * 2. 加载失败, 则委托给Spring加载
     * 3. 加载失败，则继续委托给当前bundle加载
     * 4. 失败, 抛出异常
     *  
     */
    @Override
    public ClassLoader getClassLoader() {
    	if (this.cbdcl == null) {
    		this.cbdcl = new CurrentBundleDelegatingClassLoader(this.currentBundle, super.getClassLoader());
    	}
    	
    	ServiceState state = this.ssm.getState(this.currentBundle.getBundleId());
    	if (state != null) {
    		state.setClassLoader(this.cbdcl);
    	}
    	
    	return cbdcl;
    }
    
    /**
     * 通过检测配置的class类型, 决定用那个全局配置
     * 
     * @param root
     * @return
     */
    @SuppressWarnings("unchecked")
	private RoleService getXmlConfigFromClassType(Element root, ClassLoader cl) {    	
		List<Element> beans = XmlParseUtil.getChildElements(root, "bean");
		if (beans != null) {
			for (Element bean : beans) {
				String clazzName = XmlParseUtil.getAttributeValue(bean, "class");
				if (clazzName != null) {
					try {												
						Class clazz = cl.loadClass(clazzName);
						if (clazz != null) {
							ServiceRole role = (ServiceRole)AnnotationUtil.findDeepAnnotationForClass(clazz, ServiceRole.class, ServiceRoleIgnore.class);
							if (role != null) {
								return new RoleService(role.value(), role);
							}
							
							Method[] ms = AnnotationUtil.findAnnotationMethodsFor(clazz, ServiceRole.class);
							if (ms != null) {
								for (Method m : ms) {
									role = (ServiceRole)m.getAnnotation(ServiceRole.class);
									if (role != null) {
										return new RoleService(role.value(), role);
									}
								}
							}
							
							if (TableDispatchService.class.isAssignableFrom(clazz)) {
								// 表类型
								return new RoleService(Role.TABLE, null);
							}
							
							if (AcceptorServiceAdaptor.class.isAssignableFrom(clazz)) {
								// socket server
								return new RoleService(Role.SOCKET_SERVER, null);
							}
							
							if (ConnectorServiceAdaptor.class.isAssignableFrom(clazz)) {
								// socket client
								return new RoleService(Role.SOCKET_CLIENT, null);
							}
						}
					} catch (ClassNotFoundException e) {
						throw new RuntimeException(e);
					}
				}
			}
		}
		
		return null;
    }
    
    /**
     * 通过检测bean名称决定用那个全局配置
     * 
     * @param root
     * @return
     */
    private RoleService getXmlConfigFromBeanName(Element root) {
    	List<Element> beans = XmlParseUtil.getChildElements(root, "bean");
		if (beans != null) {
			for (Element bean : beans) {
				String id = XmlParseUtil.getAttributeValue(bean, "id");
				if (id != null) {
					if (id.equals("tableDispatchService")) {
						return new RoleService(Role.TABLE, null);
					}
					
					if (id.equals("Acceptor")) {
						return new RoleService(Role.SOCKET_SERVER, null);
					}
					
					if (id.equals("Connector")) {
						return new RoleService(Role.SOCKET_CLIENT, null);
					}
				}
			}
		}
		    	
    	return null;
    }
    
    /**
	 * 重新处理配置文件
	 * 
	 * @param locations
	 * @return
	 */
	private Resource[] postProcessLocations(String[] locations) {
		boolean xmlHasPublicConfig = false;
		Resource moduleResource = null;		
		List<Resource> resourcesOldAll = new ArrayList<Resource>();
		List<Resource> resourcesAll = new ArrayList<Resource>();
		List<Resource> resourcesAllFor = new ArrayList<Resource>();
				
		for (int i = 0; i < locations.length; i ++) {
			try {
				Resource[] resources = getResources(locations[i]);
				if (resources != null) {
					for (Resource resource : resources) {
						resourcesAllFor.add(resource);
						String resourceStr = resource.getURI().toString();
						if (resourceStr.endsWith("module-tabledispatch-public.xml")) {
							xmlHasPublicConfig = true;
						} else if (resourceStr.endsWith("module-server-public.xml")) {
							xmlHasPublicConfig = true;
						} else if (resourceStr.endsWith("module-client-public.xml")) {
							xmlHasPublicConfig = true;
						}
						
						if (resourceStr.endsWith("module.xml")) {
							moduleResource = resource;
						}
						
						resourcesOldAll.add(resource);
					}
				}
			} catch (IOException e) {
				throw new RuntimeException("Get resources for location '" + locations[i] + "'", e);
			}
		}
				
		// 不存在-public.xml配置, 且有module.xml文件
		if (!xmlHasPublicConfig) {			
			// 分析module.xml
			try {
				Resource rs = null; 
				if (moduleResource != null) {
					rs = getXmlPublicDefineResource(moduleResource);
				} 

                if (rs == null) {
					for (Resource r : resourcesAllFor) {
						rs = getXmlPublicDefineResource(r);
						if (rs != null) {
							break;
						}
					}
				}
				
				if (rs != null) {					
					resourcesAll.add(rs);
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		
		if (FRAMEWORK_CONFIG_RESOURCE != null) {
			resourcesAll.add(FRAMEWORK_CONFIG_RESOURCE);
		}
		resourcesAll.addAll(resourcesOldAll);
		
		return resourcesAll.toArray(new Resource[resourcesAll.size()]);
	}
	
	public Resource[] getResources(String locationPattern) throws IOException {
		if (locationPattern.equals(FrameworkConstants.ROLE_WEBAPP_CONFIG)) {			
			// 将公用xml文件加入配置
			List<ServiceRoleResource> srr = TYPECONIG_MAP.get(Role.WEBAPP);
			if (srr != null) {
				List<Resource> ress = new ArrayList<Resource>();
				for (ServiceRoleResource sr : srr) {
					// 比较注解是否一致
					ServiceRole r = sr.getServiceRole();
					if (r.value() == Role.WEBAPP) {
						ress.add(sr.getResource());
					}
				}
				return ress.toArray(new Resource[ress.size()]);
			}
		}
		return super.getResources(locationPattern);
	}
	
	/**
	 * 获取角色
	 * 
	 * @param moduleResource
	 * @return
	 * @throws Exception 
	 * @throws IOException 
	 */
	public RoleService getRoleFor(Resource moduleResource, final ClassLoader cl) throws Exception {
		Document doc = XmlParseUtil.get(moduleResource.getInputStream());
		if (doc != null) {
			AbstractXmlDecoder decoder = new AbstractXmlDecoder() {
				@SuppressWarnings("unchecked")
				@Override
				public Object doDecode(Document document) {					
					Element root = document.getRootElement();
					if (root != null) {								
						RoleService roleService = getXmlConfigFromClassType(root, cl);
						if (roleService == null) {									
							roleService = getXmlConfigFromBeanName(root);
						}
						
						return roleService;
					}
					
					return null;
				}
			};
			
			return (RoleService)decoder.decode(doc);
		}
		
		return null;
	}
	
	/**
	 * 获取公共定义
	 * 
	 * @param moduleResource
	 * @return
	 * @throws Exception
	 */
	private Resource getXmlPublicDefineResource(Resource moduleResource) throws Exception {
		RoleService role = getRoleFor(moduleResource, getClassLoader());
		if (role == null) {
			return null;
		}
		
		ServiceRole s = role.getServiceRole();
		// 将公用xml文件加入配置
		List<ServiceRoleResource> srr = TYPECONIG_MAP.get(role.getRole());
		if (srr != null) {
			for (ServiceRoleResource sr : srr) {
				// 比较注解是否一致
				ServiceRole r = sr.getServiceRole();	
				if (r == null || s == null) {
					return sr.getResource();
				}
				if (r.value() == s.value() && r.property().equals(s.property())) {
					return sr.getResource();
				}
			}
		}

		return null;

	}
	
	/**
	 * 覆盖原有方法，增加公用配置文件
	 */
    @Override
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    	String[] configLocations = getConfigLocations();
    	// 重新处理spring配置文件
    	Resource[] resources = postProcessLocations(configLocations);     	
    	reader.loadBeanDefinitions(resources);
    }
}
